﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AddIn.EA.Framework;
using AddIn.EA.Architecture.CatalogService.BusinessEntity;
using AddIn.EA.Architecture.Validation.BusinessEntity;

namespace AddIn.EA.Architecture.CatalogService.BusinessLogic
{
    public class DataContractXml
    {
        private RepositoryManager repository { get; set; }
        private Package service { get; set; }

        public DataContractXml(RepositoryManager repository, Package service)
        {
            this.repository = repository;
            this.service = service;
        }

        /// <summary>
        /// Gera os XMLs dos enums
        /// </summary>
        /// <returns></returns>
        public List<MemberBase> GetEnumXmls()
        {
            var enumElements = GetEnumElements();
            var enumXmls = new List<MemberBase>();

            if (enumElements == null)
                return null;

            foreach (var enumElement in enumElements)
            {
                enumXmls.Add(new MemberType()
                {
                    ProjectGUID = this.repository.ProjectGUID,
                    Name = enumElement.GetFullName(),
                    Summary = enumElement.Notes
                });

                foreach (var enumItemElement in enumElement.Attributes)
                {
                    enumXmls.Add(new MemberField()
                    {
                        ProjectGUID = this.repository.ProjectGUID,
                        Name = enumItemElement.GetFullName(),
                        Summary = enumItemElement.Notes
                    });
                }
            }

            return enumXmls;
        }

        /// <summary>
        /// Retorna os elementos dos enums
        /// </summary>
        /// <returns></returns>
        private List<Element> GetEnumElements()
        {
            var dataContract = this.service.GetPackageByName("DataContract");
            if (dataContract == null || dataContract.GetPackageByName("Enum") == null)
                return null;

            var enumElements =
                (from el in dataContract.GetPackageByName("Enum").Elements
                 where el.Type == ElementType.Class &&
                     el.Stereotype == "enumeration"
                 select el).ToList();

            return enumElements;
        }
        
        /// <summary>
        /// Gera os XMLs dos datacontracts
        /// </summary>
        /// <param name="package"></param>
        /// <returns></returns>
        public List<MemberBase> GetDataContractXmls(string package)
        {
            var dataContractElements = GetDataContractElements(package);
            var dataContractXmls = new List<MemberBase>();

            if (dataContractElements == null)
                return null;

            foreach (var dataContractElement in dataContractElements)
            {
                // Adicionar elemento e propriedades
                AddMember(dataContractXmls, dataContractElement);

                // Buscar elemento pai nas classes base
                var parentDataContractElement = GetParentDataContractElement(dataContractElement);

                // Verifica se ja nao foi adicionado
                if (parentDataContractElement != null &&
                    dataContractXmls
                        .Where(x => x.Name == parentDataContractElement.GetFullName())
                        .SingleOrDefault() == null)
                {
                    // Definir summary
                    parentDataContractElement.Notes = "Classe base dos namespaces Request e Response";

                    // Adicionar elemento pai e propriedades
                    AddMember(dataContractXmls, parentDataContractElement);
                }
            }

            return dataContractXmls;
        }

        /// <summary>
        /// Adiciona uma classe e suas propriedades
        /// </summary>
        /// <param name="dataContractXmls"></param>
        /// <param name="dataContractElement"></param>
        private void AddMember(List<MemberBase> dataContractXmls, Element dataContractElement)
        {
            // Adicionar classe
            dataContractXmls.Add(new MemberType()
            {
                ProjectGUID = this.repository.ProjectGUID,
                Name = dataContractElement.GetFullName(),
                Summary = dataContractElement.Notes
            });

            // Adicionar info do construtor padrão
            dataContractXmls.Add(new MemberMethod()
            {
                ProjectGUID = this.repository.ProjectGUID,
                Name = dataContractElement.GetFullName() + ".#ctor",
                Summary = "Contrutor padrão da classe " + dataContractElement.Name
            });

            // Buscar propriedades do elemento
            var propertyElements = (from el in dataContractElement.Methods
                                    where el.Scope == ElementScope.Public &&
                                       el.Stereotype == "property"
                                    select el).ToList();

            // Adicionar propriedades no XML
            foreach (var propertyElement in propertyElements)
            {
                dataContractXmls.Add(new MemberProperty()
                {
                    ProjectGUID = this.repository.ProjectGUID,
                    Name = propertyElement.GetFullName(),
                    Summary = propertyElement.GetNotes()
                });
            }
        }

        /// <summary>
        /// Se existir retorna o pai do elemento
        /// </summary>
        /// <param name="dataContractElement"></param>
        /// <returns></returns>
        private Element GetParentDataContractElement(Element dataContractElement)
        {
            var dataContract = this.service.GetPackageByName("DataContract");
            Element parentDataContractElement = null;

            // Verificar se existe heranca
            if (dataContractElement.Connectors != null && dataContractElement.Connectors.Count > 0)
            {
                var generalizationConnector = dataContractElement.Connectors.Where(x => x.Type == Connector.ConnectorType.Generalization).SingleOrDefault();

                if (generalizationConnector != null)
                {
                    // Se sim buscar elemento pai nas classes base
                    parentDataContractElement = dataContract.GetElementById(generalizationConnector.SupplierID);
                }
            }

            return parentDataContractElement;
        }

        /// <summary>
        /// Retorna os elementos dos datacontracts
        /// </summary>
        /// <param name="package"></param>
        /// <returns></returns>
        private List<Element> GetDataContractElements(string package)
        {
            var dataContract = this.service.GetPackageByName("DataContract");
            if (dataContract == null || dataContract.GetPackageByName(package) == null)
                return null;

            var dataContractElements =
                (from el in dataContract.GetPackageByName(package).Elements
                 where el.Type == ElementType.Class &&
                     el.Stereotype == "DataContract"
                 select el).ToList();

            return dataContractElements;
        }
    
    }
}
